package com.gopay.common.util.xml.xmlutils.tools;

import java.util.ArrayList;
import java.util.List;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;

import com.gopay.common.util.xml.constants.BeanType;
import com.gopay.common.util.xml.utils.Assert0;
import com.gopay.common.util.xml.utils.XmlBeansUtils;
import com.gopay.common.util.xml.utils.validator.tools.ValidatorTools;
import com.gopay.common.util.xml.xmlutils.message.Message;
import com.gopay.common.util.xml.xmlutils.message.xml.vo.BFNode;
import com.gopay.common.util.xml.xmlutils.message.xml.vo.Bean;
import com.gopay.common.util.xml.xmlutils.message.xml.vo.Field;

/**
 * 
 * @author liujy
 *
 */
public class X2JUtilTools {
	
	private X2JUtilTools(){
		
	}
	
	/**
	 * 
	 * @param message
	 * @return
	 * @throws CheckMessageException 
	 */
	static Object builderObj(Message message, String xml) throws Exception {
		
		Assert0.notNull(message, "The message must not be null.");
		Assert0.notEmpty4String(xml, "The xml must not be null.");

		try {
			Document xmlDoc = DocumentHelper.parseText(xml);
			
			List<Bean> beans = message.getBeans();
			Assert0.notEmpty4List(beans, "The beans must not be empty.");
			
			Object data = null;
			for(int i = 0, size = beans.size(); i < size; i++){
				Bean bean = beans.get(i);
				if(BeanType.beanTypeMain.toString().equals(bean.getBeanType())){
					data = parseMainBean(bean, xmlDoc);
					break;
				}
			}
			
			Assert0.notNull(data, "The data must not be null, xml: " + xml);
			
			for(int i = 0, size = beans.size(); i < size; i++){
				Bean bean = beans.get(i);
				if(!BeanType.beanTypeMain.toString().equals(bean.getBeanType())){
					parseOtherBean(data, bean, xmlDoc);
				}
			}
			return data;
		}catch (Throwable e) {
			throw new RuntimeException("builder java object error, msgType："
					+ message.getMsgType() + ", xml: "
					+ xml, e);
		}
	}
	
	/**
	 * 
	 * @param bean
	 * @param xmlDoc
	 * @throws Exception 
	 */
	private static Object parseMainBean(Bean bean, Node node) throws Exception{
		
		Assert0.notNull(bean, "The bean must not be null.");
		Assert0.notEmpty4String(bean.getBeanType(), "The bean.beanType must not be empty");
		
		Object beanMain = XmlBeansUtils.createObjInstance(bean.getType());
		builderBean(beanMain, bean.getNodes(), node);
		return beanMain;
	}
	
	/**
	 * 
	 * @param bean
	 * @param xmlDoc
	 * @throws Exception 
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static void parseOtherBean(Object data, Bean bean, Node node) throws Exception{
		
		Assert0.notNull(bean, "The bean must not be null.");
		Assert0.notEmpty4String(bean.getBeanType(), "The bean.beanType must not be empty");

		List list = new ArrayList();
		List<Element> listE = node.selectNodes(bean.getPath());
		for(int i = 0, size = listE == null ? 0 : listE.size(); i < size; i++){
			Object beanList = XmlBeansUtils.createObjInstance(bean.getType());// 构建基类，强转使用克隆技术 
			builderBean(beanList, bean.getNodes(), listE.get(i));
			list.add(beanList);
		}

		XmlBeansUtils.setPropertyObject(data, bean.getName(), List.class.getName(), list);
	}
	
	/**
	 * 
	 * @param obj
	 * @param bean
	 * @param node
	 * @throws Exception
	 */
	private static void builderBean(Object obj, List<BFNode> beanNodes, Node node) throws Exception{
		for(int k = 0, size2 = beanNodes == null ? 0 : beanNodes.size(); k < size2; k++){
			builderField(obj, beanNodes.get(k), node);
		}
	}
	
	@SuppressWarnings("unchecked")
	private static void builderField(Object obj, BFNode beanNode, Node node) throws Exception{
		
		Assert0.notNull(beanNode, "The bean.node must not be null.");
		
		if(beanNode instanceof Bean){
			parseOtherBean(obj, (Bean)beanNode, node);
			return;
		}
		Field f = (Field)beanNode;
		Object value = null;
		if(f.isElement()){
			List<Element> fieldE = node.selectNodes(beanNode.getPath());
			if(null == fieldE || fieldE.isEmpty()){
				throw new RuntimeException(beanNode.getPath() + "不存在");
			}
			value = fieldE.get(0).getText();
		}else{
			String path = beanNode.getPath();
			int index = path.lastIndexOf("/");
			String aName = null;
			if(-1 == index){
				Attribute a = ((Element)node).attribute(path);
				value = a.getText();
			}else{
				aName = path.substring(0, index) + "/@" + path.substring(index + 1);
				Attribute a = (Attribute)node.selectSingleNode(aName);
				value = a.getText();
			}
		}
		XmlBeansUtils.setPropertyObject(obj, beanNode.getName(), beanNode.getType(), value);
		
		// 校验数据
        validator(obj, beanNode);
	}
	
	/**
     * 
     * @param obj
     * @param field
     * @throws CheckMessageException 
     */
    private static void validator(Object obj, BFNode node) throws Exception{
        ValidatorTools.validator(node, obj);
    }
}